<html>
	<head>
		<script src="js/Three.js"></script>
		<script src="js/custom/ThreeExtras.js"></script>
		<script src="js/Stats.js"></script>
			<title>OpenCloth Textured WebGL Demo by Movania Muhammad Mobeen</title>
	</head>
	<body>
        <div align=right><p><b>OpenCloth Textured WebGL Demo</b><br>
		                       Integrator: Explicit Euler <br>
		                        Step size: 1/60</p></div>
	</body>
	<script type="text/javascript">
		var camera, scene, renderer,
		    geometry, material, mesh, plane,
		    light, cube;
		var stats;
		var oldX, oldY, rX, rY, dist, state, D2R;
		var simulate = true;
		var EPSILON = 0.0001;
		var EPSILON2 = EPSILON*EPSILON;
		springs=new Array();
		F=new Array();
		V=new Array();
		var numX = 20;
		var numY = 20;
		var pickedPoint = -1;
		DEFAULT_DAMPING= 60   ;
		GRAVITY=new THREE.Vector3(0,-9.8,0);
		mass = 1;
		dt =  1.0/60;

		init();
		animate();

        function Spring () {
           	this.p1=-1;
           	this.p2=-1;
           	this.rest_length=0;
           	this.Ks=0.5;
           	this.Kd=0.5;
        }

		function AddSpring(a, b, ks, kd) {
			var spring=new Spring();
			spring.p1=a;
			spring.p2=b;
			spring.Ks=ks;
			spring.Kd=kd;
			var deltaP = new THREE.Vector3();
			deltaP.sub(mesh.geometry.vertices[a].position,mesh.geometry.vertices[b].position);
			spring.rest_length = Math.sqrt(deltaP.dot(deltaP));
			springs.push(spring);
		}

        function init() {
            var info = document.createElement('div');
            info.style.position = 'absolute';
            info.style.top = '10px';
            info.style.width = '100%';
            info.style.textAlign = 'center';
            document.body.appendChild(info);

			rX = 75;
			rY = 90;
			dist = 10;
			oldX = 0;
			oldY = 0;
			state = -1;
			D2R   = 0.0174532925199433;
			size = 4; //world space size of cloth
	        hsize = size/2;
			u     = numX +1;
			v     = numY +1;

			KsStruct = 1000;
			KdStruct = 0.5;
			KsShear = 1000;
			KdShear = 0.5;
			KsBend = 1000;
			KdBend = 0.5;

	       	camera = new THREE.PerspectiveCamera( 60, window.innerWidth / window.innerHeight, 0.1, 100 );

			scene = new THREE.Scene();
			scene.add(camera);

			//draw axes
			var geoX = new THREE.Geometry();
			var geoY = new THREE.Geometry();
			var geoZ = new THREE.Geometry();

			geoX.vertices.push(new THREE.Vertex(new THREE.Vector3(0, 0, 0)));
			geoX.vertices.push(new THREE.Vertex(new THREE.Vector3(1, 0, 0)));

			geoY.vertices.push(new THREE.Vertex(new THREE.Vector3(0, 0, 0)));
			geoY.vertices.push(new THREE.Vertex(new THREE.Vector3(0, 1, 0)));

			geoZ.vertices.push(new THREE.Vertex(new THREE.Vector3(0, 0, 0)));
			geoZ.vertices.push(new THREE.Vertex(new THREE.Vector3(0, 0, 1)));

			var lineX = new THREE.Line(geoX, new THREE.LineBasicMaterial({ color: 0xff0000, linewidth: 3 }));
			var lineY = new THREE.Line(geoY, new THREE.LineBasicMaterial({ color: 0x00ff00, linewidth: 3 }));
			var lineZ = new THREE.Line(geoZ, new THREE.LineBasicMaterial({ color: 0x0000ff, linewidth: 3 }));

			scene.add(lineX);
			scene.add(lineY);
			scene.add(lineZ);

			geometry = new THREE.PlaneGeometry( -20, 20, 10, 10);
			plane = new THREE.Mesh(  geometry,new THREE.MeshBasicMaterial( { color: 0x0000ff, wireframe:true } ));
			plane.rotation.x = 90 * D2R;
			scene.add( plane );
			geometry = new THREE.Geometry();

			//Fill in vertices of cloth mesh
			for(var j=0;j<v;j++) {
				for(var i=0;i<u;i++) {
				    var tmp = new THREE.Vertex(new THREE.Vector3( ((i/(u-1)) *2.0-1.)* hsize,size+1, ((j/(v-1.0) )* size)));
				    tmp.uv = new THREE.UV( 	i / (u-1), 	j / (v-1) );

					F.push(new THREE.Vector3(0,0,0));
					V.push(new THREE.Vector3(0,0,0));
					geometry.vertices.push(tmp);
				}
			}

		    //Now fill in face information
		    for (var i = 0; i < numY; i++) {
				for (var j = 0; j < numX; j++) {
					i0 = i * (numX+1) + j;
					i1 = i0 + 1;
					i2 = i0 + (numX+1);
					i3 = i2 + 1;
					/*
					if ((j+i)%2) {
					    var face1 = new THREE.Face3(i0, i2, i1);
					    var face2 = new THREE.Face3(i1, i2, i3);
					    geometry.faces.push(face1);
					    geometry.faces.push(face2);
					} else {
						var face1 = new THREE.Face3(i0, i2, i3);
						var face2 = new THREE.Face3(i0, i3, i1);
						geometry.faces.push(face1);
						geometry.faces.push(face2);
					}
					*/
					//using Face4 instead of Face3 based on suggestion of Mr.Doob
					var face = new THREE.Face4(i0, i2, i3, i1);
					geometry.faces.push(face);
					geometry.faceVertexUvs[0].push([
					geometry.vertices[i0].uv,
					geometry.vertices[i2].uv,
					geometry.vertices[i3].uv,
					geometry.vertices[i1].uv
						]);

				}
			}

	        geometry.computeFaceNormals();
	        geometry.computeVertexNormals();
			geometry.computeCentroids();

			mesh = new THREE.Mesh( geometry,new THREE.MeshPhongMaterial( { ambient: 0xFFFFFF, map: THREE.ImageUtils.loadTexture( "opencloth.png" ) } )); //new THREE.MeshNormalMaterial() );
			mesh.doubleSided = true;
			mesh.geometry.dynamic = true; //for WebGL renderer
			scene.add( mesh );

			light = new THREE.PointLight(0xdddddd);
			light.position.set(0, 3, 25);
			scene.add(light);

			//setup springs
			// Horizontal
			for (l1 = 0; l1 < v; l1++)	// v
				for (l2 = 0; l2 < (u - 1); l2++) {
					AddSpring((l1 * u) + l2,(l1 * u) + l2 + 1,KsStruct,KdStruct);
				}

			// Vertical
			for (l1 = 0; l1 < (u); l1++)
				for (l2 = 0; l2 < (v - 1); l2++) {
					AddSpring((l2 * u) + l1,((l2 + 1) * u) + l1,KsStruct,KdStruct);
				}

			// Shearing Springs
			for (l1 = 0; l1 < (v - 1); l1++)
				for (l2 = 0; l2 < (u - 1); l2++) {
					AddSpring((l1 * u) + l2,((l1 + 1) * u) + l2 + 1,KsShear,KdShear);
					AddSpring(((l1 + 1) * u) + l2,(l1 * u) + l2 + 1,KsShear,KdShear);
				}

			// Bend Springs
			for (l1 = 0; l1 < (v); l1++) {
				for (l2 = 0; l2 < (u - 2); l2++) {
					AddSpring((l1 * u) + l2,(l1 * u) + l2 + 2,KsBend,KdBend);
				}
				AddSpring((l1 * u) + (u - 3),(l1 * u) + (u - 1),KsBend,KdBend);
			}

			for (l1 = 0; l1 < (u); l1++) {
				for (l2 = 0; l2 < (v - 2); l2++) {
					AddSpring((l2 * u) + l1,((l2 + 2) * u) + l1,KsBend,KdBend);
				}
				AddSpring(((v - 3) * u) + l1,((v - 1) * u) + l1,KsBend,KdBend);
			}

			console.log("Total springs: "+springs.length);
			console.log("Total forces: "+F.length);
			console.log("Total velocities: "+V.length);

	       //	renderer = new THREE.CanvasRenderer();
	       	renderer = new THREE.WebGLRenderer();
	       	renderer.setSize( window.innerWidth, window.innerHeight );

	      	document.body.appendChild( renderer.domElement );

	       	stats = new Stats();
	       	stats.domElement.style.position = 'absolute';
	       	stats.domElement.style.top = '0px';
	       	document.body.appendChild(stats.domElement);

	       	document.addEventListener( 'mousedown', onMouseDown, false );
	       	document.addEventListener( 'mousemove', onMouseMove, false );
	       	document.addEventListener( 'mouseup', onMouseUp, false );
	   	}

		function onMouseDown( event ) {
			event.preventDefault();
			//0 left mouse button
			//1 middle mouse button
			//2 right mouse button
			state = event.button;

			oldX = event.clientX;
			oldY = event.clientY;

			if (state == 0) {
			}
		}

		function onMouseMove(event) {
			if(state == 1) {
				dist = dist * (1 + (event.clientY - oldY)/60.0);
			} else if(state == 0){
				rY += (event.clientX - oldX)/5.0;
				rX += (event.clientY - oldY)/5.0;
			}

			oldX = event.clientX;
			oldY = event.clientY;
			render();

		}

		function onMouseUp( event ) {
			event.preventDefault();
			//console.log("Rx: "+rX+",Ry: "+rY);
			state = -1;
		}

		function computeForces() {
		   for(var i=0;i<mesh.geometry.vertices.length;i++) {
		      F[i].x=0;
		      F[i].y=0;
		      F[i].z=0;
		      if(i!=0 && i!= numX)
		         F[i].addSelf(GRAVITY);

		      F[i].subSelf( V[i].multiplyScalar(DEFAULT_DAMPING));
		   }

	   	   //add spring forces
		   var deltaP = new THREE.Vector3();
		   var deltaV = new THREE.Vector3();
		   for(var i=0;i<springs.length;i++) {
		   		var p1 = mesh.geometry.vertices[springs[i].p1].position;
		   		var p2 = mesh.geometry.vertices[springs[i].p2].position;
		   		var v1 = V[springs[i].p1];
		   		var v2 = V[springs[i].p2];
		   		deltaP.sub(p1,p2);
		   		deltaV.sub(v1,v2);
		   		var dist = deltaP.length();
	            //console.log("p1: "+p1+", p2: "+p2+" Dis: "+dist);
		   		var leftTerm  = -springs[i].Ks * (dist-springs[i].rest_length);
		   		var rightTerm = -springs[i].Kd * ((deltaV.dot(deltaP))/dist);
		   		var springForce = (deltaP.normalize()).multiplyScalar(leftTerm + rightTerm);
		   		if(springs[i].p1 != 0 && springs[i].p1 != numX)
		   			F[springs[i].p1].addSelf(springForce);
		   		if(springs[i].p2 != 0 && springs[i].p2 != numX )
		   			F[springs[i].p2].subSelf(springForce);
			}
		}

		function integrateEuler() {
			var deltaTimeMass = dt/ mass;
	        var N = mesh.geometry.vertices.length;
	        var oldP = mesh.geometry.vertices[N-1].position.clone();
	        geometry.__dirtyVertices = true;
		 	for(var i=0;i<N;i++) {
				var oldV = V[i];
				V[i].addSelf(F[i].multiplyScalar(deltaTimeMass));
				mesh.geometry.vertices[i].position.addSelf(oldV.multiplyScalar(dt));
				if(mesh.geometry.vertices[i].position.y <0) {
					mesh.geometry.vertices[i].position.y = 0;
				}
			}

			var diff = new THREE.Vector3(0,0,0);
			diff.sub(oldP, mesh.geometry.vertices[N - 1].position);

			simulate = (diff.lengthSq()> EPSILON2);
	        //
			//console.log("Diff: " + diff.length());
		}

		function doPhysics() {
	  	   	computeForces();
	  	   	//console.log("Force: "+F[1].z+" Vel: "+V[1].z);
	  	   	integrateEuler();
		}

	   	function animate() {
	   		requestAnimationFrame(animate);
			if (simulate) {
		       	geometry.computeFaceNormals();
	       		geometry.computeVertexNormals();
	       		geometry.__dirtyNormals = true;
		        doPhysics();
			}

			render();
			stats.update();
		}

	   	function render() {
	   	/*
			mesh.position.z = dist;
			mesh.rotation.x = rX*D2R;
			mesh.rotation.z = rY*D2R;
	        plane.position.z = dist;
	        plane.rotation.x = rX*D2R;
	        plane.rotation.z = rY*D2R;
		*/
			var theta = rY * D2R;
		   	var phi = rX * D2R;
		   	var radius = dist;
		   	camera.position.x = radius * Math.sin(phi) * Math.cos(theta);
		   	camera.position.y = radius * Math.cos(phi);
		   	camera.position.z = radius * Math.sin(phi) * Math.sin(theta);
		   	camera.lookAt(new THREE.Vector3(0, 0, 0));

			renderer.render( scene, camera );
		}

		</script>
</html>